#include <linux/sched.h>
#include <linux/kernel.h>

#include "blk.h"

struct request request[NR_REQUEST];

struct wait_queue *wait_for_request = NULL;

/* blk_dev_struct is:
 *	do_request-address
 *	next-request
 */
struct blk_dev_struct blk_dev[NR_BLK_DEV] = {
    {NULL, NULL}, /* no_dev */
    {NULL, NULL}, /* dev mem */
    {NULL, NULL}, /* dev fd */
    {NULL, NULL}, /* dev hd */
    {NULL, NULL}, /* dev ttyx */
    {NULL, NULL}, /* dev tty */
    {NULL, NULL}  /* dev lp */
};

static void add_request(struct blk_dev_struct *dev, struct request *req) {
    struct request *tmp;

    req->next = NULL;
    cli();
    if (!dev->current_request) {
        dev->current_request = req;
        sti();
        (dev->request_fn)();
        return;
    }
    for (tmp = dev->current_request; tmp->next; tmp = tmp->next)
        if ((IN_ORDER(tmp, req) ||
             !IN_ORDER(tmp, tmp->next)) &&
            IN_ORDER(req, tmp->next))
            break;
    req->next = tmp->next;
    tmp->next = req;
    sti();
}

static void make_request(int major, int rw, struct buffer_head *bh) {
    struct request *req;

    if (rw != READ && rw != WRITE)
        panic("Bad block dev command, must be R/W/RA/WA");

repeat:
    /* we don't allow the write-requests to fill up the queue completely:
     * we want some room for reads: they take precedence. The last third
     * of the requests are only for reads.
     */
    if (rw == READ)
        req = request + NR_REQUEST;
    else
        req = request + ((NR_REQUEST * 2) / 3);
    /* find an empty request */
    while (--req >= request)
        if (req->dev < 0)
            break;
    /* if none found, sleep on new requests: check for rw_ahead */
    if (req < request) {
        sleep_on(&wait_for_request);
        goto repeat;
    }
    /* fill up the request-info, and add it to the queue */
    req->dev = bh->b_dev;
    req->cmd = rw;
    req->sector = bh->b_blocknr << 1;
    req->nr_sectors = 2;
    req->buffer = bh->b_data;
    req->bh = bh;
    req->next = NULL;
    add_request(major + blk_dev, req);
}

void ll_rw_block(int rw, struct buffer_head *bh) {
    unsigned int major;

    if ((major = MAJOR(bh->b_dev)) >= NR_BLK_DEV ||
        !(blk_dev[major].request_fn)) {
        printk("Trying to read nonexistent block-device\n\r");
        return;
    }
    make_request(major, rw, bh);
}

void blk_dev_init(void)
{
    int i;

    for (i = 0; i < NR_REQUEST; i++) {
        request[i].dev = -1;
        request[i].next = NULL;
    }
}
